package com.allwees.core.wf;

import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.BeanUtil;
import com.allwees.core.wf.event.WfActionEvent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;

/**
 * 简单工作流实现 - 有限状态机（FSM）版本
 *
 * @author yangylsky
 */
@SuppressWarnings("rawtypes")
public interface Workflow<S extends FsmState, A extends Action> {
	String getBsKey();

	/**
	 * 获取当前工作流状态
	 *
	 * @return
	 */
	<FS extends FsmState> FS getState();

	/**
	 * 设置当前工作流状态
	 *
	 * @param state
	 * @return
	 */
	void setState(S state);

	/**
	 * 设置可返回的工作流状态
	 *
	 * @param backState
	 * @return
	 */
	void setBackState(S backState);

	/**
	 * 设置可返回的工作流状态
	 *
	 * @param latestAction
	 * @return
	 */
	void setLatestAction(A latestAction);

	/**
	 * 设置Entity状态
	 *
	 * @param status
	 * @return
	 */
	void setEStatus(EStatus status);

	/**
	 * 当前工作流是否已经开始
	 *
	 * @return
	 */
	default boolean started() {
		return getState() != null && getState().started();
	}

	/**
	 * 当前工作流是否已经结束（已经结束的流程不能再变迁）
	 *
	 * @return
	 */
	default boolean ended() {
		return getState().ended();
	}

	/**
	 * 启动流程(无条件变迁)
	 *
	 * @return
	 */
	default S start() {
		if(started()) {
			throw new WfStateException("当前流程已经启动，无需再次启动");
		}
		return signal();
	}

	/**
	 * 启动流程(带条件变迁)
	 *
	 * @return
	 */
	default S start(A action) {
		if(started()) {
			throw new WfStateException("当前流程已经启动，无需再次启动");
		}
		return signal(action);
	}

	/**
	 * 对当前状态节点发送一个无条件变迁信号，变迁到下一个对应的状态节点。注：当前状态节点的状态变迁定义必须支持无条件变迁
	 *
	 * @return
	 */
	default S signal() {
		return signal(null);
	}

	/**
	 * <ol>对当前状态节点发送一个变迁信号，根据参数变迁到下一个对应的状态节点，同时执行以下动作：
	 * 		<li>设置backState；</li>
	 * 		<li>设置latestAction；</li>
	 * 		<li>如果{@link Action#afterActionStatus()}有值，则设置当前Workflow对象的status；</li>
	 * 		<li>如果{@link Action#bizType()}值不为空，则发送对应的WfActionEvent；</li>
	 * </ol>
	 * @param action
	 * @return
	 */
	@SuppressWarnings("unchecked")
	default S signal(A action) {
		S currentState = getState();
		S nextState = (S) currentState.transit(action);
		if(nextState != null && !nextState.equals(currentState)) { //变迁状态与当前状态不等才作回退记录
			setBackState(currentState);
			setState(nextState);
			currentState = nextState;
		}
		if(action != null) {
			setLatestAction(action);
			if(!"WorkflowInstance".equalsIgnoreCase(getClass().getSimpleName())) {
				if(action.afterActionStatus() != null) {
					setEStatus(action.afterActionStatus());
				} else if (currentState.ended()) {
					setEStatus(EStatus.COMPLETED);
				}
			}
			if(StringUtils.isNoneBlank(action.bizType())) {
				ApplicationContext appContext = BeanUtil.getApplicationContext();
				if(appContext != null) {
					appContext.publishEvent(new WfActionEvent<>(getBsKey(), action));
				}
			}
		}
		return nextState;
	}
}
